#!/bin/bash

echo_success() {
  echo -n "["
  echo -en "\\033[1;32m"
  echo -n $" O  K "
  echo -en "\\033[0;39m"
  echo -n "]"
  echo -n " $1"
  echo -ne "\n"
  echo "[ O  K ]  $1" >> $SETUP_LOGFILE
  return 0
}

echo_error() {
  echo ""
  echo -n "["
  echo -en "\\033[1;31m"
  echo -n $"ERROR "
  echo -en "\\033[0;39m"
  echo -n "]"
  echo -n " $1"
  echo -ne "\n"
  echo "[ERROR ] ******>>> $1" >> $SETUP_LOGFILE
  return 1
}

echo_start() {
  echo ""
  echo ""
  echo -n "["
  echo -en "\\033[1;34m"
  echo -n $"START "
  echo -en "\\033[0;39m"
  echo -n "] ---------------- "
  echo -n " $1"
  echo -ne "\n"
  echo "" >> $SETUP_LOGFILE
  echo "[START ] ----> $1" >> $SETUP_LOGFILE
  return 0
}
echo_passed() {
  echo -n "["
  echo -en "\\033[1;34m"
  echo -n $"PASSED"
  echo -en "\\033[0;39m"
  echo -n "] "
  echo -n " $1"
  echo -ne "\n"

  echo -ne "\\033[1;34m>>>>>>>>  Press any key to continue ... ...\\033[0;39m"
  read tmpanykey

  echo "[PASSED] <---- $1" >> $SETUP_LOGFILE
  return 0
}

echo_warn() {
  echo -n "["
  echo -en "\\033[1;33m"
  echo -n $"WARN  "
  echo -en "\\033[0;39m"
  echo -n "]"
  echo -n " $1"
  echo -ne "\n" 
  echo "[WARN  ] ******>>> $1" >> $SETUP_LOGFILE
  return 0
}

echo_trace() {
  if [ "x$HRSETUPTRACE" = "xTRUE" ]; then
  echo -n "["
  echo -en "\\033[1;35m"
  echo -n $"Trace "
  echo -en "\\033[0;39m"
  echo -n "]"
  echo -n " $1"
  echo -ne "\n"
  fi
  return 0
}

echo_info() {
  echo -n "["
  echo -n $"INFO  "
  echo -n "]"
  echo -n " $1"
  echo -ne "\n"
  echo "[INFO  ]  $1" >> $SETUP_LOGFILE
  return 0
}

echo_doing() {
  if [ "x$2" = "x" ]; then
    echo -ne "  $1"
  else
    echo -ne "$1"
  fi
}
echo_done() {
  if [ "x$1" = "x" ]; then
    echo "    DONE"
  else
    echo "$1"
  fi
}

# 各参数：错误提示信息
function die() {
  #echo "die>> : $@"
  if [ $# -gt 0 ]; then
    echo_error "$@"
  fi
  echo ""
  echo -en "\\033[1;31m"
  echo -n "Setup Abort! "
  echo -en "\\033[0;39m"
  echo "TIPS <<< You can edit SETUP_SILENT of install_env.conf, modify the successful steps to SKIP >>>"
  echo ""
  exit 1
}
function die_insh(){
  # 生成一个错误标识文件。在父shell中检查这个文件是否存在来判断子shell是否出错了。
  echo "`date +%y/%m/%d` `date +%H:%M:%S`" > /tmp/hrs_install_subshell_fail.flg
  #echo "die_insh>> : $@"
  die "$@"
}
function die_insh_check(){
  if [ -f /tmp/hrs_install_subshell_fail.flg ]; then
    # 存在这个文件，说明子shell中调用了die_insh函数。
    # 为防止再次执行时误判断，所以立即删除这个文件。
    rm -f /tmp/hrs_install_subshell_fail.flg
    exit 1
  fi
}

# 第一个参数：主机名或ip。如果是存储了多个主机的变量，需要用双引号括起来，否则无法循环了
# 第二个参数：主机名称，用于出错画面提示用
function check_host()
{
  if [ $# -lt 1 ]; then die "check_host fail : missing args"; fi
  if [ $# -lt 2 ] || [ "x$1" = "x" ]; then
    # 只有1个输入参数或第一个参数为空，说明第一个参数的主机名（或IP）为空，那么就跳过这个主机的检查
    echo_warn "=================== >>>>>>>>>>>>>>>>>>>> [$2] checking SKIP, First Arg value is null! "
    return 1
  fi
  local value_arr=($1)
  local all_ok=1
  for i in ${value_arr[@]}; do 
    ping -c 2 $i > /dev/null 2>&1
    if [ $? -ne 0 ];then
      echo_error "Destination Host [$2 : $i] Unreachable ,pelese check."
      all_ok=0
    fi
  done
  if [ $all_ok -ne 1 ]; then
    return 1
  fi
  echo_info "Host [$1] for [$2] passed."
  return 0
}

# 第1个参数：主机名。多个主机，用英文空格分隔。内部使用ssh登陆这个主机，为了检查端口
# 第2个参数：端口。多个端口，用英文空格分隔。
# 参数必须用【双引号】引起来。因为，对于用回车方式列出来的多个主机或端口，没有双引号括起来，无法循环
function check_port()
{
  #hosts_arr=($1) # 因为conf文件中目前用的是换行，所以暂时不需要逗号方式
  ports_arr=($2)

  local all_ok=1
  for k in $1; do
    echo_doing "check host($k) ... ... " 
    for i in ${ports_arr[@]}; do 
      ssh -o StrictHostKeyChecking=no $k "netstat -tpnl | grep -w ${i} > /dev/null"
      if [ $? -eq 0 ];then
        echo_warn "Host \"${k}\" PORT : [ ${i} ] is already occupied, pelese check."
        all_ok=0
      else
        echo_doing " [$i pass]"
      fi
    done
    echo_done " end"
  done
  if [ $all_ok -ne 1 ]; then
    echo_doing "some port occupied, Abort this installation? [y/n] : "
    while true; do 
      read inputWord_Confirm
      case $inputWord_Confirm in 
       "y" | "Y" )
        echo "Install abort"
        exit 1
        ;;
        "n" | "N" )
        return 1
        ;;
        *)
        ;;
      esac
    done
  fi
}

function check_installed()
{
  if hash $1 2>/dev/null; then
    return 0
  else
    die "$1 is not exist, please install first"
  fi
}

function check_tools()
{
  check_installed bash
  check_installed tar
  check_installed yum
  check_installed ssh
  check_installed scp
  # install openssl for base64
  # for creating local mirror repositories
  #check_installed createrepo
  #check_installed reposync
  #check_installed yum-utils
}

function yum_install()
{
  # 用yum已经把该软件包安装了
  yum list installed | egrep "^${1}" >> $SETUP_LOGFILE
  if [ $? -eq 0 ]; then
    return 0
  fi

  # 用rpm已经把该软件包安装了
  rpm -qa | egrep "^${1}" >> $SETUP_LOGFILE
  if [ $? -eq 0 ]; then
    return 0
  fi

  # 系统级软件已经安装过，比如gcc, ssh等
  if hash $1 2>/dev/null; then
    return 0
  else
    echo "Install $1 by yum :" >> $SETUP_LOGFILE
    echo_doing "install $1 ... ..."
    yum -y install $1 >> $SETUP_LOGFILE
    if [ $? -ne 0 ]; then
      die "install [$1] fail"
    fi
    echo_done
    echo "Install $1 by yum done." >> $SETUP_LOGFILE
  fi
}

# 第一个参数：画面提示信息
# 第二个参数：默认选项（可空）
function confirm_operate()
{
  # echo "arg : $1"
  if [ "x$1" = "x" ]; then
    die "missing args in confirm_operate function."
  fi

  # 判断是否静默安装
  # 输入参数替换空格为下划线
  local checked_step=$(echo "$1"|sed 's/ /_/g')
  # echo "checked_step=$checked_step"
  for i in $SETUP_SILENT; do
    local cur_step_name=$(echo "$i"|sed -e 's/=.*//')
    # echo "cur_step_name=$cur_step_name, cur_step_value=$cur_step_value"
    if [ "$cur_step_name" = "$checked_step" ]; then
      local cur_step_value=$(echo "$i"|sed -e 's/.*=//')
      # echo "cur_step_value=$cur_step_value"
      break
    fi
  done
  # echo "cur_step_value=$cur_step_value"
  local is_silent=$(echo "$cur_step_value"|sed -e 's/:.*//')
  # echo "is_silent=$is_silent"
  
  if [ "x$is_silent" = "xY" ]; then
    # 判断是否需要跳过当前步骤
    local is_skip=$(echo "$cur_step_value"|sed -e 's/.*://')
    if [ "x$is_skip" = "xSKIP" ]; then
      return 1
    else
      return 0
    fi
  fi

  echo ""
  echo -n "["
  echo -en "\\033[1;33m"
  echo -n $"Confirm"
  echo -en "\\033[0;39m"
  echo -n "] : "
  echo -ne "${1}"
  echo -n "  [y/n] : "
  if [ "x$2" != "x" ]; then
    echo -n "(default $2) "
    read inputDefault
    # 如果为空（意味着用户直接敲的回车），那么使用默认值
    if [ "x$inputDefault" = "x" ]; then
      inputDefault="$2"
    fi
    if [ "$inputDefault" = "y" ] || [ "$inputDefault" = "Y" ]; then
      unset inputDefault
      return 0
    else
      unset inputDefault
      return 1
    fi
  fi
  while true; do 
    read inputWord_Confirm
    case $inputWord_Confirm in 
      "y" | "Y" )
      unset inputWord_Confirm
      return 0
      ;;
      "n" | "N" )
      unset inputWord_Confirm
      return 1
      ;;
      *)
      ;;
    esac
  done
}

function mkdir_check()
{
  if [ "x$1" = "x" ]; then die "must input dirname, installing abort"; fi
  if [ ! -d "$1" ]; then
    mkdir -p $1 || { die "mkdir [$1] fail, installing abort"; }
  fi
}

# 目录存在则删除目录下所有内容，不存在则创建
function init_dir()
{
  if [ "x$1" = "x" ]; then die "must input dirname, installing abort"; fi
  if [ -d "$1" ]; then
    if [ "$1" = "/" ] || [ "$1" = "/bin" ] || [ "$1" = "/etc" ] || [ "$1" = "/boot" ] || [ "$1" = "/var" ] || [ "$1" = "/sys" ] || [ "$1" = "/lib" ] || [ "$1" = "/lib64" ] || [ "$1" = "/opt" ] || [ "$1" = "/sbin" ] || [ "$1" = "/dev" ]; then
      die "[$1] is WRONG, installing abort"
    else
      rm -rf $1/* || { die "do empty dir [$1] fail, installing abort"; }
    fi
  else
    mkdir_check $1
  fi
}
if [ "$dir" = "/" ]; then echo wrong; fi
checkIPAddr()
{
  echo "$1"|grep "^[0-9]\{1,3\}\.\([0-9]\{1,3\}\.\)\{2\}[0-9]\{1,3\}$" > /dev/null
  #IP地址必须为全数字
  if [ $? -ne 0 ]; then
    return 1
  fi
  local ipaddr=$1
  #以"."分隔，取出每个列的值
  local a=`echo $ipaddr|awk -F . '{print $1}'`
  local b=`echo $ipaddr|awk -F . '{print $2}'`
  local c=`echo $ipaddr|awk -F . '{print $3}'`
  local d=`echo $ipaddr|awk -F . '{print $4}'`
  for num in $a $b $c $d
  do
    #每个数值必须在0-255之间
    if [ $num -gt 255 ] || [ $num -lt 0 ]; then
      return 1
    fi
  done
  return 0
}

# 使用more显示文件内容，以便用于确认其正确性。
# 第一参数：文件全路径名
show_file_forcheck()
{
  echo ""
  echo_doing "==> Check file :$1, [y/n, default NO] : "
  read inputYesNo
  if [ "x$inputYesNo" = "xy" ]; then
    more $1
    echo ""
    echo       "*******************************************"
    echo_doing "* Everything is OK ? [y/n, default NO] : "
    echo       "*******************************************"
    while true; do
      read inputWord_Confirm
      case $inputWord_Confirm in 
       "y" | "Y" )
        return 0
        ;;
        "n" | "N" )
        die "Install abort"
        ;;
        *)
        ;;
      esac
    done
  fi
}

# 第1个参数：端口
# 第2个参数：0-等待启动成功，1-等待停止成功
# 第3个参数：提示信息
# 第4个参数：主机名。如果有这个参数，将通过ssh到远程主机上执行命令
waiting_proc_status()
{
  echo_doing "waiting [$1] for $3 ..."
  local nums=0
  while true ; do
    if [ -n "$4" ]; then
      ssh -o StrictHostKeyChecking=no $4 "netstat -tpnl | grep -w $1 > /dev/null 2>&1"
    else
      netstat -tpnl | grep -w $1 > /dev/null 2>&1
    fi
    if [ $? -ne $2 ]; then
      nums=$(expr $nums + 1 )
      if [ $nums -gt 15 ]; then
        die "$3 fail, Installing Abort ! using [ netstat -tpnl | grep -w $1 ] check."
      else
        sleep 1
        echo_doing ".."
      fi
    else
      sleep 1
      echo_doing "."
      sleep 1
      echo_doing "."
      break
    fi
  done
  echo_done
}

# 使用 kill 结束一个进程
# 第1个参数：代表这个进程的标识，全字精确匹配。比如：进程的名字等
# 第2个参数：代表这个进程的标识，模糊匹配。比如：kafka等
# 返回0：表示确切的kill掉了进程
function kill_proc()
{
  if [ "x$1" = "x" ]; then
    echo_warn "Missing proc name."
    return 1
  fi
  if [ "x$2" = "x" ]; then
    local PID=`ps -ef | grep -w "$1" | grep -v grep | awk '{print $2}'`
  else
    local PID=`ps -ef | grep -w "$1" | grep "$2" | grep -v grep | awk '{print $2}'`
  fi
  if [ -n "$PID" ]; then
    echo_info "killed [$1] proc : `echo $PID`"
    for iPID in `echo $PID`; do
      kill -9 $iPID
    done
    sleep 1
    return 0
  else
    echo_warn "proc [$1] not exist."
    return 2
  fi
}

# 向 .bash_profile 添加环境变量
# 第1个参数：环境变量的名字。如果只有这一个参数，则视为设置了一段注释文字
# 第2个参数：环境变量的值
# 第3个参数：用户名。没传入则使用当前用户的$HOME获得主目录
set_bash_profile()
{
  if [ $# -ne 3 ]; then
    echo "missing args";
    while [ $# -gt 0 ]; do
      echo "arg=$1"
      shift
    done
    exit 1;
  fi

  local USER_HOME_DIR=`grep "^${3}:" /etc/passwd | cut -d : -f 6` || { die "get ${3} USER_HOME_DIR fail"; }
  if [ "x${USER_HOME_DIR}" = "x" ]; then die "user [${3}]'s HOME dir not exist"; fi

  # 第2个参数为"HRSCOMMENT"，意味着设置注释信息
  if [ "$2" = "HRSCOMMENT" ]; then
    local tmp_value=`grep "^#[[:space:]]*${1}" $USER_HOME_DIR/.bash_profile`
    if [ -z "$tmp_value" ]; then
      echo "" >> $USER_HOME_DIR/.bash_profile
      echo "# ${1}" >> $USER_HOME_DIR/.bash_profile
    fi
    return 0
  fi

  # 设置环境变量
  sed -i "/^[[:space:]]*${1}=.*/d" $USER_HOME_DIR/.bash_profile || { die "set profile [${1}] fail"; }
  echo "${1}=${2}" >> $USER_HOME_DIR/.bash_profile
  # 复查
  local tmp_new_value=`grep "^[[:space:]]*${1}=.*" $USER_HOME_DIR/.bash_profile | sed -e 's/.*=//'`
  if [ "x$tmp_new_value" != "x${2}" ]; then
    die "set [${1}=${2}] fail, because new_value is [${tmp_new_value}]."
  fi
}

# 检查密码是否符合要求，以免在脚本中进行各种变量替换查找时出问题。
# 第1个参数：存储密码的变量名
# 第2个参数：变量值（也就是被检查的密码）。密码只能是：大小写字符、数字、@、%
check_password()
{
  if [ "x$1" = "x" ]; then die "missing parameter."; exit 1; fi
  if [ "x$2" = "x" ]; then echo_warn "$1 is null, skip this checking."; return 1; fi
  local checked_passwdstr=$(echo "$2" | tr -d '[a-zA-Z0-9@%]')
  if [ -z "$checked_passwdstr" ]; then
    return 0
  else
    die "$1 password [$2] is illegal"
  fi
}

# 给指定主机设置免密登录
# 第一个参数：需免密登录的主机名
# 第二个参数：登录密码（可空，默认使用install_env.conf中的ROOT_PASSWD）
set_nopwdlogin_host()
{
  if [ $# -lt 1 ]; then die "set_nopwdlogin_host fail : missing args"; fi
  if [ "x$1" = "x" ]; then
    die "set_nopwdlogin_host fail : missing host"
  fi
  local host_passwd="$ROOT_PASSWD"
  if [ "x$2" != "x" ]; then
    host_passwd="$2"
  fi
  echo_info "Checking nopasswd login to $1 ... ..."
      expect <<EOF
set timeout -1
spawn ssh -o StrictHostKeyChecking=no $1 "ls -l ~ >/dev/null"
expect {
"*(yes/no)*" {send  "yes\r";exp_continue}
"*assword:*" {exit 77}
eof        {exit 0}}
EOF
    if [ $? -eq 0 ]; then
      echo_trace "host [$1] has been set nopasswd login."
      return 0
    else
      echo
      echo
      echo_info "set nopasswd login to $1 ... ..."
      expect <<EOF
set timeout -1
spawn ssh-copy-id -i /root/.ssh/id_rsa.pub root@$1
expect {
"*(yes/no)*" {send  "yes\r";exp_continue}
"*assword:*" {send  "${host_passwd}\r";exp_continue}
eof        {exit 0}}
EOF
      if [ $? -ne 0 ]; then
        die "set nopasswd login to $1 FAIL"
      else
        echo_info "set nopasswd login to $1 ... ... DONE"
        return 0
      fi
    fi
}